﻿using System;
using System.Diagnostics;
using System.ServiceModel;
using Microsoft.ServiceBus;
using Microsoft.ServiceBus.Samples;

namespace MyAnt
{
	public class CloudTraceListener : TraceListener
	{
		ChannelFactory<ITraceChannel> traceChannelFactory;
		ITraceChannel traceChannel;
		object writeMutex;
		int maxRetries = 1;

		public CloudTraceListener(string servicePath, string serviceNamespace, string issuerName, string issuerSecret)
		{
			writeMutex = this;

			//Construct a Service Bus URI
			var uri = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, servicePath);

			//Create a Behavior for the Credentials
			var sharedSecretServiceBusCredential = new TransportClientEndpointBehavior
				{
					CredentialType = TransportClientCredentialType.SharedSecret
				};
			sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = issuerName;
			sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret = issuerSecret;

			//Create a Channel Factory
			traceChannelFactory = new ChannelFactory<ITraceChannel>(new NetEventRelayBinding(), new EndpointAddress(uri));
			traceChannelFactory.Endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
		}

		public override bool IsThreadSafe
		{
			get
			{
				return true;
			}
		}

		public override void Close()
		{
			traceChannel.Close();
			traceChannelFactory.Close();
		}

		private void LockWrapper(Action action)
		{
			lock (writeMutex)
			{
				int retry = 0;
				for (; ; )
				{
					EnsureChannel();
					try
					{
						action.Invoke();
						return;
					}
					catch (CommunicationException)
					{
						if (++retry > maxRetries)
						{
							throw;
						}
					}
				}
			}
		}

		public override void Write(string message)
		{
			LockWrapper(() => traceChannel.Write(message));
		}

		public override void WriteLine(string message)
		{
			LockWrapper(() => traceChannel.WriteLine(message));
		}

		private void EnsureChannel()
		{
			if (traceChannel != null &&
					traceChannel.State == CommunicationState.Opened)
			{
				return;
			}

			if (traceChannel != null)
			{
				traceChannel.Abort();
				traceChannel = null;
			}

			traceChannel = traceChannelFactory.CreateChannel();
			traceChannel.Open();
		}
	}
}